package com.njtoyo.taxi.admin.service.marketing.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njtoyo.taxi.admin.cache.PrimaryCachePutter;
import com.njtoyo.taxi.admin.library.Const;
import com.njtoyo.taxi.admin.logic.marketing.AdminGiveCustomerCouponLogLogic;
import com.njtoyo.taxi.admin.mapper.master.entity.Coupon;
import com.njtoyo.taxi.admin.mapper.master.enums.CouponUsageCondition;
import com.njtoyo.taxi.admin.mapper.master.mapper.CouponMapper;
import com.njtoyo.taxi.admin.mapper.master.mapper.UserMapper;
import com.njtoyo.taxi.admin.mongo.entity.primary.CouponMongo;
import com.njtoyo.taxi.admin.mongo.entity.secondary.AdminGiveCustomerCouponLogMongo;
import com.njtoyo.taxi.admin.mongo.repository.primary.CouponMongoRepository;
import com.njtoyo.taxi.admin.mongo.repository.secondary.AdminGiveCustomerCouponLogMongoRepository;
import com.njtoyo.taxi.admin.rest.presenter.marketing.coupon.AdminGiveCustomerCouponLogListPresenter;
import com.njtoyo.taxi.admin.rest.presenter.marketing.coupon.CouponCustomerCantonPresenter;
import com.njtoyo.taxi.admin.rest.presenter.marketing.coupon.CouponHotListPresenter;
import com.njtoyo.taxi.admin.rest.wrapper.marketing.coupon.*;
import com.njtoyo.taxi.admin.service.business.notify.AppCustomerNotifyService;
import com.njtoyo.taxi.admin.service.marketing.CouponService;
import com.njtoyo.taxi.entity.backend.AdminUser;
import com.taxi.entity.common.RestResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Component
@Slf4j
@DS(Const.PRIMARY_DS)
public class CouponServiceImpl implements CouponService {

    @Autowired
    private CouponMapper couponMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private CouponMongoRepository couponMongoRepository;

    @Autowired
    private AdminGiveCustomerCouponLogMongoRepository adminGiveCustomerCouponLogMongoRepository;

    @Autowired
    private AppCustomerNotifyService appCustomerNotifyService;

    @Autowired
    private PrimaryCachePutter primaryCachePutter;

    @Autowired
    private AdminGiveCustomerCouponLogLogic adminGiveCustomerCouponLogLogic;

    @Override
    @Transactional
    public RestResult customerGiveCoupon(AdminUser adminUser, CreateWrapper wrapper) {
        // 校验发券
        for (CreateCouponWrapper coupon : wrapper.getCoupons()) {
            // 校验必填
            if (Objects.isNull(coupon.getCouponCategory()) || Objects.isNull(coupon.getCouponScope()) || Objects.isNull(coupon.getCouponDiscountType()) || Objects.isNull(coupon.getCouponDiscountDigit()) || Objects.isNull(coupon.getStartedAt()) || Objects.isNull(coupon.getEndedAt())) {
                return RestResult.failed("优惠券类型category、适用范围、优惠券类型discountType、折扣或减免金额、开始时间、结束时间不能为空");
            }

            // 校验折扣率
            if (coupon.getCouponDiscountType() && coupon.getCouponDiscountDigit() >= 10) {
                return RestResult.failed("折扣类型,折扣率不能超过10");
            }

            // 若限制类型存在,则限制额必填
            if (Objects.nonNull(coupon.getUsageCondition()) && Objects.isNull(coupon.getUsageLimitDigit())) {
                return RestResult.failed("缺少使用限制usageLimitDigit参数");
            }

            // 若限制类型为满减,则限制额须大于优惠金额
            if (Objects.nonNull(coupon.getUsageCondition()) && coupon.getUsageCondition().equals(CouponUsageCondition.full) && Double.parseDouble(coupon.getUsageLimitDigit()) <= coupon.getCouponDiscountDigit()) {
                return RestResult.failed("限制额须大于优惠金额");
            }

            // 验证优惠券类型，折扣类型，限制类型设置是否合法
            int checkDigit;
            switch (coupon.getCouponCategory()) {
                case reduction:
                    if (coupon.getCouponDiscountType()) {
                        return RestResult.failed("优惠券配置非法");
                    }
                    checkDigit = NumberUtil.compare(coupon.getCouponDiscountDigit(), 0);
                    if (checkDigit == 0 || checkDigit == -1) {
                        return RestResult.failed("减免金额不能小于等于0");
                    }
                    break;
                case fullreduction:
                    if (coupon.getCouponDiscountType() || !coupon.getUsageCondition().equals(CouponUsageCondition.full)) {
                        return RestResult.failed("优惠券配置非法");
                    }

                    int checkUsageDigit = NumberUtil.compare(Double.parseDouble(coupon.getUsageLimitDigit()), 0);
                    if (checkUsageDigit == 0 || checkUsageDigit == -1) {
                        return RestResult.failed("使用限制金额不能小于等于0");
                    }

                    checkDigit = NumberUtil.compare(coupon.getCouponDiscountDigit(), 0);
                    if (checkDigit == 0 || checkDigit == -1) {
                        return RestResult.failed("减免金额不能小于等于0");
                    }
                    break;
                case discount:
                    if (!coupon.getCouponDiscountType() || !coupon.getUsageCondition().equals(CouponUsageCondition.max)) {
                        return RestResult.failed("优惠券配置非法");
                    }

                    checkDigit = NumberUtil.compare(coupon.getCouponDiscountDigit(), 0);
                    if (checkDigit == 0 || checkDigit == -1) {
                        return RestResult.failed("折扣不能小于等于0");
                    }
                    break;
            }

            // 验证开始、结束时间
            Date today = DateUtil.beginOfDay(new Date());
            if (coupon.getStartedAt().before(today) || coupon.getEndedAt().isBeforeOrEquals(coupon.getStartedAt())) {
                return RestResult.failed("开始、结束时间错误");
            }
        }

        // 准备发券的用户数量
        Integer targetCustomerCount = wrapper.getCustomers().size();

        // 准备给每个用户发的券数量
        Integer taskCouponCount = wrapper.getCoupons().size();

        // 本次批量发券总数量
        Integer batchGiveCouponCount = targetCustomerCount * taskCouponCount;

        // 日志
        log.info("管理员ID: {}", adminUser.getId());
        log.info("计划发券{}人*{}张，共{}张", targetCustomerCount, taskCouponCount, batchGiveCouponCount);
        log.info("乘客明细：{}", wrapper.getCustomers());
        log.info("券明细：{}", wrapper.getCoupons());

        log.info("开始插入mysql:coupon");
        // 构建数据,人=》券
        List<CouponCustomerCantonPresenter> targetCustomers = userMapper.getUserAndCantonForCoupon(wrapper.getCustomers());

        List<Coupon> couponInsertMysql = new ArrayList<>();
        for (CouponCustomerCantonPresenter customer : targetCustomers) {
            for (CreateCouponWrapper coupon : wrapper.getCoupons()) {
                // 构建插入mysql数据
                Coupon couponMysql = new Coupon();
                if ("free".equals(wrapper.getActiveKey())) {
                    couponMysql.setSaleCategory(0);
                } else {
                    couponMysql.setSaleCategory(1);
                }
                couponMysql.setCategory(coupon.getCouponCategory());
                couponMysql.setScope(coupon.getCouponScope());
                couponMysql.setPlatform(coupon.getCouponPlatform());
                couponMysql.setDigit(coupon.getCouponDiscountDigit());
                couponMysql.setDiscountType(coupon.getCouponDiscountType());
                couponMysql.setDiscountDigit(coupon.getCouponDiscountDigit());
                couponMysql.setUserId(customer.getId());
                couponMysql.setCantonId(customer.getCantonId());
                couponMysql.setCustomerRegionId(customer.getCustomerRegionId());
                couponMysql.setGrantedTimestamp(System.currentTimeMillis() / 1000);

                // 满减或折扣
                if (Objects.nonNull(coupon.getUsageCondition())) {
                    couponMysql.setUsageCondition(coupon.getUsageCondition());
                    couponMysql.setUsageLimit(coupon.getUsageLimitDigit());

                    if (coupon.getUsageCondition().equals(CouponUsageCondition.full)) {
                        // full 满减
                        couponMysql.setLimitation(MessageFormat.format("FULL.{0}", coupon.getUsageLimitDigit()));
                    } else if (coupon.getUsageCondition().equals(CouponUsageCondition.max)) {
                        // discount 折扣
                        couponMysql.setLimitation(MessageFormat.format("MAX.{0}", coupon.getUsageLimitDigit()));
                    }
                }

                couponMysql.setStartedAt(coupon.getStartedAt());
                couponMysql.setEndedAt(coupon.getEndedAt());

                couponInsertMysql.add(couponMysql);
            }
        }

        // 批量插入mysql
        Integer createCount = couponMapper.batchCreate(couponInsertMysql);

        log.info("mysql插入成功，条数：{}", createCount);

        List<CouponMongo> couponInsertMongo = new ArrayList<>();
        List<AdminGiveCustomerCouponLogMongo> logInsertMongo = new ArrayList<>();
        // 构建插入mongo数据
        for (Coupon coupon : couponInsertMysql) {
            CouponMongo couponMongo = new CouponMongo();

            couponMongo.setCouponId(coupon.getId().intValue());
            couponMongo.setCategory(coupon.getCategory().getCode());
            couponMongo.setPlatform(coupon.getPlatform().getCode());
            couponMongo.setScope(coupon.getScope().getCode());
            couponMongo.setDiscountType(coupon.getDiscountType() ? 1 : 0);
            couponMongo.setDiscountDigit(coupon.getDiscountDigit());
            couponMongo.setLimitType(Objects.nonNull(coupon.getUsageCondition()) ? coupon.getUsageCondition().getCode() : null);
            couponMongo.setLimitDigit(Objects.isNull(coupon.getUsageLimit()) ? null : Double.parseDouble(coupon.getUsageLimit()));
            couponMongo.setCustomerId(coupon.getUserId().intValue());
            couponMongo.setCantonId(Convert.toInt(coupon.getCantonId()));
            couponMongo.setCustomerRegionId(Convert.toInt(coupon.getCustomerRegionId()));
            couponMongo.setHadUsed(false);

            long startAt = coupon.getStartedAt().getTime() / 1000;
            long endAt = coupon.getEndedAt().getTime() / 1000;
            couponMongo.setStartedTimestamp((int) startAt);
            couponMongo.setEndedTimestamp((int) endAt);

            couponMongo.setGrantedTimestamp(coupon.getGrantedTimestamp().intValue());
            couponMongo.setCreatedAt(new Date());
            couponMongo.setUpdatedAt(new Date());

            couponInsertMongo.add(couponMongo);

            // mongo日志
            AdminGiveCustomerCouponLogMongo logMongo = new AdminGiveCustomerCouponLogMongo();
            logMongo.setCustomer_id(coupon.getUserId().intValue());

            // 获取乘客姓名、手机号
            for (CouponCustomerCantonPresenter user : targetCustomers) {
                if (user.getId().equals(coupon.getUserId())) {
                    logMongo.setCustomer_name(user.getName());
                    logMongo.setCustomer_mobile_number(user.getMobileNumber());

                    break;
                }
            }

            logMongo.setAdmin_id(adminUser.getId());
            logMongo.setAdmin_account(adminUser.getAccount());
            logMongo.setAdmin_name(adminUser.getName());
            logMongo.setAdmin_mobile_number(adminUser.getMobileNumber());
            logMongo.setCoupon_id(coupon.getId().intValue());
            logMongo.setCategory(coupon.getCategory().getCode());
            logMongo.setPlatform(coupon.getPlatform().getCode());
            logMongo.setScope(coupon.getScope().getCode());
            logMongo.setDiscount_type(coupon.getDiscountType() ? 1 : 0);
            logMongo.setDiscount_digit(coupon.getDiscountDigit());
            logMongo.setLimit_type(Objects.nonNull(coupon.getUsageCondition()) ? coupon.getUsageCondition().getCode() : null);
            logMongo.setLimit_digit(Objects.isNull(coupon.getUsageLimit()) ? null : Double.parseDouble(coupon.getUsageLimit()));
            logMongo.setCreated_at(coupon.getGrantedTimestamp().intValue());

            logInsertMongo.add(logMongo);
        }
        log.info("开始插入mongo:primary-coupon");
        // 插入mongo（primary-coupon）
        couponMongoRepository.insert(couponInsertMongo);

        log.info("开始插入mongo:second-admin_give_customer_coupon_log");
        // 插入mongo（second-admin_give_customer_coupon_log）
        adminGiveCustomerCouponLogMongoRepository.insert(logInsertMongo);

        log.info("开始更新用户通知");
        List<Long> userIds = targetCustomers.stream().map(CouponCustomerCantonPresenter::getId).collect(Collectors.toList());
        appCustomerNotifyService.updateCustomerNotifyForCoupon(userIds);
        log.info("更新用户通知结束");

        log.info("开始清理用户缓存");
        for (CouponCustomerCantonPresenter user : targetCustomers) {
            primaryCachePutter.deleteCustomerWalletById(user.getId());
        }
        log.info("清理用户缓存结束");

        return RestResult.success();
    }

    @Override
    public RestResult<PageImpl<AdminGiveCustomerCouponLogListPresenter>> adminGiveLog(AdminGiveLogQueryWrapper queryWrapper) {
        String adminSearch = queryWrapper.getAdminSearch();
        String customerSearch = queryWrapper.getCustomerSearch();

        AdminGiveLogQueryDto queryDto = new AdminGiveLogQueryDto();

        // 分类搜索
        if (Objects.nonNull(queryWrapper.getCouponCategory())) {
            queryDto.setCouponCategory(queryWrapper.getCouponCategory().getCode());
        }

        // 管理员搜索
        if (Objects.nonNull(adminSearch)) {
            if (this.isPhoneNum(adminSearch)) {
                queryDto.setAdminPhone(adminSearch);
            } else {
                queryDto.setAdminName(adminSearch);
            }
        }

        // 乘客搜索
        if (Objects.nonNull(customerSearch)) {
            if (this.isPhoneNum(customerSearch)) {
                queryDto.setCustomerPhone(customerSearch);
            } else {
                queryDto.setCustomerName(customerSearch);
            }
        }

        queryDto.setCurrent(queryWrapper.getCurrent() - 1);
        queryDto.setSize(queryWrapper.getSize());

        PageImpl<AdminGiveCustomerCouponLogListPresenter> list = adminGiveCustomerCouponLogLogic.getList(queryDto);

        return RestResult.success(list);
    }

    @Override
    public RestResult<Page<CouponHotListPresenter>> couponHot(Long customerId, CouponHotQueryWrapper wrapper) {
        Page<CouponHotListPresenter> list = couponMapper.selectPageByCustomerId(customerId, wrapper);

        return RestResult.success(list);
    }

    private Boolean isPhoneNum(String content) {
        return ReUtil.isMatch("^1[0-9]\\d{9}$", content);
    }

}
